package com.chunlin.reflect;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class ReflectTest {
    //定义
    //Java的反射（reflection）机制是在运行状态中，对于任意一个类，都能够知道这个类的所有属性和方法；对于任意一个对象，都能够调用它的任意方法和属性
    // 既然能拿到那么，我们就可以修改部分类型信息；这种动态获取信息以及动态调用对象方法的功能称为java语言的反射（reflection）机制

    //用途
    //1.在日常的第三方应用开发过程中，经常会遇到某个类的某个成员变量、方法或是属性是私有的或是只对系统应用开放
    // 这时候就可以利用Java的反射机制通过反射来获取所需的私有成员或是方法
    //2. 反射最重要的用途就是开发各种通用框架，比如在spring中，我们将所有的类Bean交给spring容器管理，无论是XML配置Bean还是注解配置
    // 当我们从容器中获取Bean来依赖注入时，容器会读取配置，而配置中给的就是类的信息，spring根据这些信息，需要创建那些Bean，spring就动态的创建这些类

    //反射基本信息
    //Java程序中许多对象在运行时会出现两种类型：运行时类型(RTTI)和编译时类型
    // 例如Person p = new Student()；这句代码中p在编译时类型为Person，运行时类型为Student
    // 程序需要在运行时发现对象和类的真实信息。而通过使用反射程序就能判断出该对象和类属于哪些类

    //反射相关的类
    //Class类        代表类的实体，在运行的Java应用程序中表示类和接口
    //Field类        代表类的成员变量/类的属性
    //Method类       代表类的方法
    //Constructor类  代表类的构造方法

    //Class类(反射机制的起源 )
    //Java文件被编译后，生成了.class文件，JVM此时就要去解读.class文件 ,被编译后的Java文件.class也被JVM解析为一个对象
    // 这个对象就是java.lang.Class 这样当程序在运行时，每个java文件就最终变成了Class类对象的一个实例
    // 我们通过Java的反射机制应用到这个实例，就可以去获得甚至去添加改变这个类的属性和动作，使得这个类成为一个动态的类


    //反射的优点
    //灵活性和通用性：
    //反射提供了极高的灵活性，允许程序在运行时动态地创建对象、调用方法、访问字段等，而不需要在编译时知道具体的类和方法。这使得可以编写更通用的代码，如各种框架和库通常做的那样。
    //解耦和扩展性：
    //反射支持在不修改原有代码的情况下，对程序进行扩展和修改，有助于降低系统各组件之间的耦合度。
    //配置驱动的应用：
    //可以通过配置文件来控制对象的创建和行为，这对于需要根据不同配置改变行为的企业应用尤其有价值。
    //便于开发通用框架：
    //许多流行的Java框架（如Spring、Hibernate等）都广泛使用反射来实现依赖注入和服务定位，提供用户友好的API

    //反射的缺点
    //性能开销：
    //反射操作通常比直接的Java方法调用要慢。因为反射涉及到类型解析、动态调度等，这些都需要在运行时进行，增加了额外的系统开销。
    //安全风险：
    //反射允许运行时修改程序执行的行为，包括访问私有成员和方法，这可能会破坏封装性，导致不恰当的行为或安全漏洞。
    //复杂性：
    //使用反射的代码通常比直接代码更难理解和维护。特别是对于非专家用户，错误地使用反射可能导致代码难以调试和测试。
    //破坏封装性：
    //反射允许代码访问类的私有字段和方法，这本质上破坏了类的封装性，可能会带来意外的副作用。

    public static void main(String[] args) throws NoSuchFieldException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
//        test1();
//        reflectNewInstance();
//        reflectPrivateConstructor();
//        reflectPrivateField();
        reflectPrivateMethod();
    }

    private static void reflectPrivateMethod() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        Class<Student> studentClass = Student.class;
        //获取私有方法
        Method studnetMethod = studentClass.getDeclaredMethod("function", String.class, Integer.class);
        System.out.println("私有方法的方法名为：" + studnetMethod.getName());
        studnetMethod.setAccessible(true);

        //获取私有构造函数,并初始化
        Constructor<Student> studentConstructor = studentClass.getDeclaredConstructor(String.class, int.class);
        studentConstructor.setAccessible(true);
        Student student = studentConstructor.newInstance("hekailong", 45);

        //调用私有方法
        studnetMethod.invoke(student, "test", 1);
    }

    private static void reflectPrivateField() throws NoSuchFieldException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        Class<Student> studentClass = Student.class;

        //获取私有构造函数并实例化对象
        Constructor<Student> studentConstructor = studentClass.getDeclaredConstructor(String.class, int.class);
        studentConstructor.setAccessible(true);
        Student student = studentConstructor.newInstance("liukun", 24);
        System.out.println(student);

        //获取私有成员变量,并修改
        Field nameField = studentClass.getDeclaredField("name");
        nameField.setAccessible(true);
        nameField.set(student, "刘坤");
        System.out.println(student);
        String name = (String) nameField.get(student);
        System.out.println("反射私有属性修改了name：" + name);

    }

    private static void reflectPrivateConstructor() {
        try {
            Class<Student> studentClass = Student.class;
            //注意传入对应的参数,getConstructor只能获取public的构造函数
            //Constructor<Student> studentClassConstructor = studentClass.getConstructor(String.class, int.class);
            //getDeclaredConstructor可以可以访问类中声明的所有构造函数，包括私有的（private）
            Constructor<Student> studentClassConstructor = studentClass.getDeclaredConstructor(String.class, int.class);

            //设置为true后可修改访问权限
            studentClassConstructor.setAccessible(true);
            Student student = studentClassConstructor.newInstance("shengming", 23);
            System.out.println("获得私有构造哈数且修改姓名和年龄："+ student);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    private static void reflectNewInstance() {
        //创建对象
        try {
            Class<?> studentClass = Class.forName("com.chunlin.reflect.Student");
            //从Java 9开始，Class.newInstance()方法已被弃用，并在后续版本中被移除
            // 这是因为newInstance()方法在处理构造函数异常时存在一些问题
            // 它只能抛出InstantiationException和IllegalAccessException，对于其他在构造函数中可能抛出的异常则不会正确处理
            //Object objectStudent = studentClass.newInstance();
            Object objectStudent = studentClass.getDeclaredConstructor().newInstance();
            Student student = (Student) objectStudent;
            System.out.println("获得学生对象: " + student);
        } catch (ClassNotFoundException e) {
            System.err.println("类没有找到异常：" + e.getMessage());
        } catch (InstantiationException e) {
            System.err.println("实例化异常，可能是抽象类或无默认构造器：" + e.getMessage());
        } catch (IllegalAccessException e) {
            System.err.println("非法访问异常，可能是构造器不可访问：" + e.getMessage());
        } catch (NoSuchMethodException e) {
            System.err.println("没有找到匹配的构造方法异常：" + e.getMessage());
        } catch (InvocationTargetException e) {
            System.err.println("构造器抛出异常：" + e.getTargetException().getMessage());
        }
    }

    private static void test1() {
        //1.通过getClass获取Class对象
        Student student1 = new Student();
        Class<?> class1 = student1.getClass();

        //2.直接通过 类名.class 的方式得到,该方法最为安全可靠，程序性能更高
        //直接使用类名加 .class 访问静态成员，它在编译时就确定了类型，完全没有运行时类型错误的风险
        //这说明任何一个类都有一个隐含的静态成员变量 class
        Class<Student> class2 = Student.class;

        //3、通过 Class 对象的 forName() 静态方法来获取，用的最多
        //但可能抛出 ClassNotFoundException 异常
        Class<?> class3 = null;
        try {
            //注意这里是类的全路径，如果有包需要加包的路径
            //这个获取class对象方式主要是因为参数是字符串的原因,所以可以读取数据,然后动态的加载类对象
            class3 = Class.forName("com.chunlin.reflect.Student");
        } catch (ClassNotFoundException exception) {
            exception.printStackTrace();
        }

        //一个类在 JVM 中只会有一个 Class 实例,即我们对上面获取的
        //c1,c2,c3进行 equals 比较，发现都是true
        System.out.println(class1.equals(class2));
        System.out.println(class1.equals(class3));
        System.out.println(class3.equals(class2));
    }
}
